#target illustrator

/**
 * Batch trace all raster images in PDF files with the "Redraw_1" preset.
 * Compatible with Adobe Illustrator 2025.
 */
(function () {
    if (app.name !== "Adobe Illustrator") {
        alert("Please run this script in Adobe Illustrator.");
        return;
    }

    var originalInteractionLevel = app.userInteractionLevel;
    var originalCoordinateSystem = app.coordinateSystem;

    app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;
    app.coordinateSystem = CoordinateSystem.ARTBOARDCOORDINATESYSTEM;

    try {
        run();
    } catch (error) {
        alert("Script failed: " + error.message);
    } finally {
        app.userInteractionLevel = originalInteractionLevel;
        app.coordinateSystem = originalCoordinateSystem;
    }
})();

function run() {
    var inputFolder = Folder.selectDialog("Select the input folder containing PDF files.");
    if (!inputFolder) {
        return;
    }

    var outputFolder = Folder.selectDialog("Select the output folder.");
    if (!outputFolder) {
        return;
    }

    var pdfFiles = inputFolder.getFiles(function (item) {
        return item instanceof File && /\.pdf$/i.test(item.name);
    });

    if (!pdfFiles || pdfFiles.length === 0) {
        alert("No PDF files found in the selected input folder.");
        return;
    }

    var processed = 0;
    var failures = [];

    for (var i = 0; i < pdfFiles.length; i++) {
        var pdfFile = pdfFiles[i];
        try {
            processPdfFile(pdfFile, outputFolder);
            processed++;
        } catch (fileError) {
            failures.push(pdfFile.displayName + ": " + fileError.message);
        }
    }

    showSummary(processed, pdfFiles.length, failures);
}

function processPdfFile(pdfFile, outputFolder) {
    var document = app.open(pdfFile);

    try {
        unlockAndReveal(document);
        traceAllRasterItems(document, "Redraw_1");

        app.selection = null;
        saveAsPdf(document, outputFolder, pdfFile.displayName);
    } finally {
        document.close(SaveOptions.DONOTSAVECHANGES);
    }
}

function unlockAndReveal(document) {
    app.selection = null;
    try {
        document.selection = null;
    } catch (_) {}

    try {
        app.executeMenuCommand("unlockAll");
    } catch (_) {}
    try {
        app.executeMenuCommand("showAll");
    } catch (_) {}
}

function traceAllRasterItems(document, presetName) {
    if (!document.rasterItems || document.rasterItems.length === 0) {
        return [];
    }

    var rasterItems = collectRasterItems(document);
    if (rasterItems.length === 0) {
        return [];
    }

    ensureTracingPresetAvailable(presetName);
    var tracedObjects = [];

    for (var i = 0; i < rasterItems.length; i++) {
        var rasterItem = rasterItems[i];
        if (!rasterItem || rasterItem.locked || rasterItem.hidden) {
            continue;
        }

        try {
            var traceResult = rasterItem.trace();
            if (!traceResult) {
                continue;
            }

            var tracingObject = applyPresetToTracing(traceResult, presetName);
            if (tracingObject) {
                tracedObjects.push(tracingObject);
            }
        } catch (error) {
            throw new Error("Image tracing failed: " + error.message);
        }
    }

    return tracedObjects;
}

function collectRasterItems(document) {
    var items = [];
    try {
        var rasterItems = document.rasterItems;
        for (var i = 0; i < rasterItems.length; i++) {
            items.push(rasterItems[i]);
        }
    } catch (_) {}
    return items;
}

function ensureTracingPresetAvailable(presetName) {
    if (!presetName || presetName === "") {
        throw new Error("Tracing preset name is empty.");
    }

    var presets;
    try {
        presets = app.tracingPresetsList;
    } catch (error) {
        throw new Error("Cannot access tracing preset list: " + error.message);
    }

    if (!presets || presets.length === 0) {
        throw new Error("No tracing presets are available in Illustrator.");
    }

    for (var i = 0; i < presets.length; i++) {
        var preset = presets[i];
        try {
            if (preset && preset.name && preset.name === presetName) {
                return;
            }
        } catch (_) {}

        if (preset === presetName) {
            return;
        }
    }

    throw new Error("Tracing preset \"" + presetName + "\" was not found.");
}

function applyPresetToTracing(traceResult, presetName) {
    if (!traceResult) {
        return null;
    }

    var tracingObject = resolveTracingObject(traceResult);
    if (!tracingObject) {
        throw new Error("Tracing result does not expose tracing options.");
    }

    var options = tracingObject.tracingOptions;
    if (!options) {
        throw new Error("Tracing options are unavailable for the traced object.");
    }

    try {
        options.loadFromPreset(presetName);
        app.redraw();
    } catch (error) {
        throw new Error("Cannot apply tracing preset \"" + presetName + "\": " + error.message);
    }

    return tracingObject;
}

function resolveTracingObject(traceResult) {
    if (!traceResult) {
        return null;
    }

    try {
        if (traceResult.tracingOptions) {
            return traceResult;
        }
    } catch (_) {}

    try {
        if (traceResult.tracing && traceResult.tracing.tracingOptions) {
            return traceResult.tracing;
        }
    } catch (_) {}

    try {
        if (traceResult.parent && traceResult.parent.tracingOptions) {
            return traceResult.parent;
        }
    } catch (_) {}

    return null;
}

function saveAsPdf(document, outputFolder, fileName) {
    var outputFile = new File(outputFolder.fsName + "/" + fileName);
    var pdfOptions = new PDFSaveOptions();
    pdfOptions.preserveEditability = true;
    document.saveAs(outputFile, pdfOptions);
}

function showSummary(processed, total, failures) {
    var message = "Processed PDF files: " + processed + " / " + total;
    if (failures.length > 0) {
        message += "\nFailed files:\n" + failures.join("\n");
    }
    alert(message);
}
